package com.baomibing.flow.model;

import com.alibaba.fastjson.annotation.JSONField;
import com.baomibing.core.exception.ServerRuntimeException;
import com.baomibing.flow.constant.NodeTypeEnum;
import com.baomibing.flow.exception.FlowExceptionEnum;
import com.baomibing.flow.execute.Execution;
import com.baomibing.flow.express.Expression;
import com.baomibing.flow.rule.Rule;
import com.google.common.collect.Maps;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

/**
 * TransitionNode
 *
 * @author frog 2023/9/18 14:23
 * @version 1.0.0
 **/
@Slf4j
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ToString @Getter
public class TransitionNode extends BaseNode {

    @JSONField(serialize = false, deserialize = false)
    private final WorkNode source;

    @JSONField(serialize = false, deserialize = false)
    private final WorkNode target;

    @Setter
    private String sourceHandle;
    @Setter
    private String targetHandle;

    @Setter
    private Expression expression;

    @Setter
    private Map<String, String> params = Maps.newHashMap();

    @Override
    public void execute(Execution execution) {
        log.info("*****process: 正在执行TransitionNode({}_{}),执行人:{}", source.getName(), target.getName(), execution.getActor());
        boolean beCompelete = false;
        if (source instanceof ForkNode) {
            beCompelete = this.flow.doForkProcess(source, execution);
        } else if (source instanceof TaskNode) {
            beCompelete = this.flow.doProcess(source, this, execution);
        } else if (source instanceof ParallelNode) {
            beCompelete = this.flow.doParallelProcess(source, execution);
        } else if (source instanceof JoinNode) {
            beCompelete = this.flow.doJoinProcess(source, execution);
        } else if (source instanceof  StartNode) {
            this.flow.doProcess(source, this, execution);
        } else {
            throw new ServerRuntimeException(FlowExceptionEnum.TRANSITION_NODE_CAN_NOT_SUPPORT_TYPE_OF_SOURCE_NODE, source.getNodeType());
        }

        if (!beCompelete) return;
        passed = true;
        if (target instanceof ParallelNode ) {
            this.flow.doParallelProcess(target, execution);
        } else if (target instanceof JoinNode) {
            this.flow.doJoinProcess(target, execution);
        } else if (target instanceof ForkNode) {
            this.flow.doForkProcess(target, execution);
        }
    }

    @Override
    public void reject(Execution execution) {
        log.info("*****reject: 正在驳回执行TransitionNode({}_{}}),执行人:{}", source.getName(), target.getName(), execution.getActor());
        boolean beComplete = false;
        if (target instanceof ForkNode) {
            beComplete = this.flow.doForkReject(target, execution);
        } else if (target instanceof TaskNode) {
            beComplete = this.flow.doReject(target, this, execution);
        } else if (target instanceof ParallelNode) {
            beComplete = this.flow.doParallelReject(target, execution);
        } else if (target instanceof JoinNode) {
            beComplete = this.flow.doJoinReject(target, execution);
        } else if (target instanceof  EndNode) {
            this.flow.doReject(target, this, execution);
        } else {
             throw new ServerRuntimeException(FlowExceptionEnum.TRANSITION_NODE_CAN_NOT_SUPPORT_TYPE_OF_TARGET_NODE, target.getNodeType());
        }

        if (!beComplete) return;
        passed = false;
        if (source instanceof ForkNode) {
            this.flow.doForkReject(source, execution);
        } else if (source instanceof ParallelNode) {
            this.flow.doParallelReject(source, execution);
        } else if (source instanceof TaskNode) {
            TaskNode tn = (TaskNode) source;
            tn.doChildReject(execution);

        }
    }

    //虚拟驳回，目的是将对应的流程状态由完成变为未完成，以便重现执行（驳回策略为TO_FILL_USER，重新提交策略为COMMIT_NEXT_STEP时调用）
    public WorkNode rejectVirtual() {
        if (target instanceof ForkNode) {
            this.flow.doForkVirtualReject(target);
        } else if (target instanceof TaskNode) {
            this.flow.doVirtualReject(target, this);
        } else if (target instanceof ParallelNode) {
            this.flow.doParallelVirtualReject(target);
        } else if (target instanceof JoinNode) {
            this.flow.doJoinVirtualReject(target);
        } else if (target instanceof  EndNode) {
            this.flow.doVirtualReject(target, this);
        }

        return source;
    }



    public TransitionNode (WorkNode source, WorkNode target) {
        this.source = source;
        this.target = target;
        this.source.getOutputs().add(this);
        this.target.getInputs().add(this);
        this.name =  Rule.rule4ActionName(source.getName(), target.getName());
        this.nodeType = NodeTypeEnum.Transit.name();
    }

    public TransitionNode(WorkNode source, WorkNode target, boolean beInput, boolean beOutput) {
        this.source = source;
        this.target = target;
        if (beOutput) {
            this.source.getOutputs().add(this);
        }
        if (beInput) {
            this.target.getInputs().add(this);
        }
        this.name = Rule.rule4ActionName(source.getName(), target.getName());// "ACTON" + "_" + source.getName() + "_" + target.getName();
        this.nodeType = NodeTypeEnum.Transit.name();
    }
}
